﻿namespace Octopus.OctopusService.Database

open System.Data
open System.Data.SqlClient
open System.Data.SqlTypes
open System.Threading
open Octopus.OctopusLib
open Octopus.OctopusService.General
open Octopus.OctopusService.Database.Core


(*-----------------------------------------------------------------------
 * MessagesToUsers functions
 *
 *)

module MessagesToUsers =

  let insertNewMessage recipient text =
    use conn = connectoToOctopusDb()
  
    let timeStr = System.DateTime.Now.ToString("yyyy/MM/dd hh:mm:ss")
    let query =
        "INSERT INTO [OctopusDb].[dbo].[MessagesToUsers]" +
        " ([Recipient], [DateTime], [Message])" +
        (sprintf " VALUES ('%s', '%s', '%s')" recipient timeStr text)
  
    try
      use cmd = new SqlCommand(query, conn, CommandTimeout=10)
      let n = cmd.ExecuteNonQuery()
      if n = 0 then failwith ("Insertion failed - " + query)
    finally
      conn.Close()


  let getMessages recipient =
    use conn = connectoToOctopusDb()
    let query = sprintf "SELECT [ID],[DateTime],[Message] FROM [OctopusDb].[dbo].[MessagesToUsers] WHERE [Recipient]='%s' ORDER BY [ID]" recipient

    try  
      use cmd = new SqlCommand(query, conn)
      use reader = cmd.ExecuteReader()

      [| while reader.Read() do
           let id = reader.GetInt32(0)
           let date = reader.GetDateTime(1)
           let text = reader.GetString(2)
           yield new UserMessage(id, recipient, date, text) |]

    finally
      conn.Close()


(*-----------------------------------------------------------------------
 * GeneralConfig functions
 *
 *)

module GeneralConfig =

  let private insertConfiguration confKey confValue =
    use conn = connectoToOctopusDb()
    use trans = conn.BeginTransaction(IsolationLevel.Serializable)
    let cmd = new SqlCommand(Connection=conn, Transaction=trans)    
  
    try
      cmd.CommandText <- sprintf "SELECT [ID] FROM [OctopusDb].[dbo].[GeneralConfig] WHERE [ConfKey]='%s'" confKey
                
      let reader = cmd.ExecuteReader()
      if reader.Read() then failwithf "Configuration %s already exists" confKey
      reader.Dispose()
      
      cmd.CommandText <- "INSERT INTO [OctopusDb].[dbo].[GeneralConfig]" +
                         " ([ConfKey],[ConfValue]) VALUES " +
                         (sprintf "('%s','%s')" confKey confValue)
                         
      cmd.ExecuteNonQuery() |> ignore
      
      trans.Commit()
      conn.Close()
    with _ ->
      trans.Rollback()
      conn.Close()
      reraise()      


  let private deleteConfiguration confKey =
    use conn = connectoToOctopusDb()
    use trans = conn.BeginTransaction(IsolationLevel.Serializable)
    let cmd = new SqlCommand(Connection=conn, Transaction=trans)    
  
    try
      cmd.CommandText <- sprintf "SELECT [ID] FROM [OctopusDb].[dbo].[GeneralConfig] WHERE [ConfKey]='%s'" confKey
                
      use reader = cmd.ExecuteReader()
      if reader.Read() then
        reader.Close()
        cmd.CommandText <- sprintf "DELETE FROM [OctopusDb].[dbo].[GeneralConfig] WHERE [ConfKey]='%s'" confKey
        cmd.ExecuteNonQuery() |> ignore
        trans.Commit()
        conn.Close()
        true
      else
        trans.Rollback()        // nothing to commit anyway
        conn.Close()
        false        

    with _ ->
      trans.Rollback()
      conn.Close()
      reraise()      


  let private getConfiguration confKey =
    use conn = connectoToOctopusDb()
    let query = sprintf "SELECT [ID],[ConfValue] FROM [OctopusDb].[dbo].[GeneralConfig] WHERE [ConfKey]='%s'" confKey

    try  
      use cmd = new SqlCommand(query, conn)
      use reader = cmd.ExecuteReader()

      if reader.Read() then
        reader.GetString(1)
      else
        failwithf "Configuration %s not found" confKey

    finally
      conn.Close()

  let get_AdminCredentialsDomain () = getConfiguration "AdminCredentialsDomain"
  let delete_AdminCredentialsDomain () = deleteConfiguration "AdminCredentialsDomain"
  let set_AdminCredentialsDomain value = insertConfiguration "AdminCredentialsDomain" value

  let get_AdminCredentialsUsername () = getConfiguration "AdminCredentialsUsername"
  let delete_AdminCredentialsUsername () = deleteConfiguration "AdminCredentialsUsername"
  let set_AdminCredentialsUsername value = insertConfiguration "AdminCredentialsUsername" value

  let get_AdminCredentialsPassword () = getConfiguration "AdminCredentialsPassword"
  let delete_AdminCredentialsPassword () = deleteConfiguration "AdminCredentialsPassword"
  let set_AdminCredentialsPassword value = insertConfiguration "AdminCredentialsPassword" value

  let get_SharedStoragePath () = getConfiguration "SharedStoragePath"
  let delete_SharedStoragePath () = deleteConfiguration "SharedStoragePath"
  let set_SharedStoragePath value = insertConfiguration "SharedStoragePath" value



(*-----------------------------------------------------------------------
 * VMTemplates functions
 *
 *)

module VMTemplates =

  let insertNewVMTemplate name description (cpuCountMin:uint16) (cpuCountMax:uint16) (memorySizeMbMin:int) (memorySizeMbMax:int) nicName vhdPath (parentId:int) isHpcNode hpcScheduler =
    use conn = connectoToOctopusDb()
  
    let q : Printf.StringFormat<string -> string -> string> =
      "INSERT INTO [OctopusDb].[dbo].[VMTemplates] (%s) VALUES (%s); SELECT SCOPE_IDENTITY()"
    
    let query = 
      if isHpcNode then
        let fields = "[IsDeleted], [Name], [Description], [CpuCountMin], [CpuCountMax], [MemorySizeMbMin], [MemorySizeMbMax], [NicSwitchName], [VhdPath], [ParentTemplateID], [IsHpcNode], [HpcScheduler]"
        let values = sprintf "'false', '%s', '%s', '%d', '%d', '%d', '%d', '%s', '%s', '%d', '%s', '%s'"
                       name description cpuCountMin cpuCountMax memorySizeMbMin memorySizeMbMax nicName vhdPath parentId "true" hpcScheduler
        sprintf q fields values
      else
        let fields = "[IsDeleted], [Name], [Description], [CpuCountMin], [CpuCountMax], [MemorySizeMbMin], [MemorySizeMbMax], [NicSwitchName], [VhdPath], [ParentTemplateID], [IsHpcNode]"
        let values = sprintf "'false', '%s', '%s', '%d', '%d', '%d', '%d', '%s', '%s', '%d', '%s'"
                       name description cpuCountMin cpuCountMax memorySizeMbMin memorySizeMbMax nicName vhdPath parentId "false"
        sprintf q fields values
  
    try
      use cmd = new SqlCommand(query, conn, CommandTimeout=10)
      //let n = cmd.ExecuteNonQuery()
      //if n = 0 then failwith ("Insertion failed - " + query)
      let id = cmd.ExecuteScalar() :?> System.Decimal
      int id
    finally
      conn.Close()


  module private VmTemplateQuery =
    let getSelect_WithDeleted =
        "SELECT [ID],[Name],[Description],[CpuCountMin],[CpuCountMax],[MemorySizeMbMin],[MemorySizeMbMax],[NicSwitchName],[VhdPath],[ParentTemplateID],[IsHpcNode],[HpcScheduler] FROM [OctopusDb].[dbo].[VMTemplates]"
    let getSelect =
        getSelect_WithDeleted + " WHERE [IsDeleted]='false'"
    let getSelectWhere cond =
        sprintf "%s WHERE [IsDeleted]='false' AND (%s)" getSelect_WithDeleted cond

  let private getVmTemplateWithQuery (cmd:SqlCommand) =
    use reader = cmd.ExecuteReader()
    try
      [| while reader.Read() do
           let id = reader.GetInt32(0)
           let name = reader.GetString(1)
           let description = if reader.IsDBNull(2) then null else reader.GetString(2)
           let cpucountmin = uint16( reader.GetInt32(3) )
           let cpucountmax = uint16( reader.GetInt32(4) )
           let memorysizemin = reader.GetInt32(5)
           let memorysizemax = reader.GetInt32(6)
           let nicname = reader.GetString(7)
           let vhdpath = reader.GetString(8)
           let parentId = reader.GetInt32(9)
           let ishpcnode = reader.GetBoolean(10)
           let hpcscheduler = if reader.IsDBNull(11) then null else reader.GetString(11)
           let template = new VMTemplate(id, name, description, cpucountmin, cpucountmax, memorysizemin, memorysizemax, nicname, vhdpath, parentId, ishpcnode, hpcscheduler)
           yield template |]
    finally
      reader.Close()


  let getVMTemplates () =
    use conn = connectoToOctopusDb()
    let query = VmTemplateQuery.getSelect
  
    try
      use cmd = new SqlCommand(query, conn)
      getVmTemplateWithQuery cmd
    finally
      conn.Close()


  let getVMTemplate templateId =
    use conn = connectoToOctopusDb()
    let cond = sprintf "[ID]='%d'" templateId
    let query = VmTemplateQuery.getSelectWhere(cond)
  
    try
      use cmd = new SqlCommand(query, conn)
      (getVmTemplateWithQuery cmd).[0]
    finally
      conn.Close()


(*-----------------------------------------------------------------------
 * Log functions
 *
 *)

module LogFile =
  let lockObject = new System.Object()
  
  let writePlainText (text:string) = 
    let logpath = (buildFilePath getServiceBinPath "log.txt").FullName
    lock lockObject ( fun() ->
         use sw = new System.IO.StreamWriter(logpath, true, AutoFlush=true)
         sw.WriteLine(text)
         sw.Close()    
      )

  let writeInLog (message:string) =
    writePlainText( sprintf "[%s] %s" (System.DateTime.Now.ToString()) message )

  let writeBlank () =
    writePlainText ""


module ClipsLogFile =
  let lockObject = new System.Object()
  
  let writePlainText (text:string) = 
    let logpath = (buildFilePath getServiceBinPath "log_clips.txt").FullName
    lock lockObject ( fun() ->
         use sw = new System.IO.StreamWriter(logpath, true, AutoFlush=true)
         sw.WriteLine(text)
         sw.Close()    
      )

  let writeInLog (message:string) =
    writePlainText( sprintf "[%s] %s" (System.DateTime.Now.ToString()) message )

  let writeBlank () =
    writePlainText ""
